import re
import sys
import urllib
from xml.dom import minidom
from waveapi import events
from waveapi import model
from waveapi import robot
from google.appengine.api import urlfetch

__author__ = "giuliani.v@gmail.com (Ivan Giuliani)"

ROBOT_NAME = "What is?"
DICT_SERVICE = "http://services.aonaware.com/DictService/DictService.asmx/Define"
MAX_DEFINITION_LENGTH = 700

def extract_whatis_term(text):
    """
    Given the text of a blip, extract the terms you want to search for a
    definition. Returns an empty list if there's no 'what is' clause or a
    list of terms to search for (one for every 'what is' clause).
    """
    # TODO: I'm sure there's a better way to write this regexp
    terms = re.findall(r"what is (?P<term>[\w]+|\"[\w ]+\")",
            text, re.IGNORECASE)

    # clean up terms: remove quotes and make everything lowercase
    return [term.lower().replace('"', '').strip()
            for term in terms]

def get_definitions(term):
    """
    Retrieve the term definition from aonaware.com. Returns an
    iterable object
    """
    xml = minidom.parse(urllib.urlopen(DICT_SERVICE, urllib.urlencode({'word': term})))
    definitions = xml.getElementsByTagName('Definition')
    for definition in definitions:
        source_tree = definition.getElementsByTagName('Dictionary')[0]
        source = source_tree.getElementsByTagName('Name')[0].firstChild.data
        define = definition.getElementsByTagName('WordDefinition')[0].firstChild.data
    
        define = ' '.join(define.split()).strip()
        if len(define) > MAX_DEFINITION_LENGTH:
            define = define[:MAX_DEFINITION_LENGTH] + "..."

        yield "%s (%s)" % (define, source)

def OnRobotAdded(properties, context):
    "Invoked when the robot is added to the wave"
    wavelet = context.GetRootWavelet()
    wavelet.CreateBlip().GetDocument().SetText(
        "I'm the \"What is?\" robot, just write \"what is <term>\" "       +
        "and I'll look out the term for you. If your term is a composed "  + 
        "one (i.e.: a two-words term or something like that) just put "    +
        "it within quotes. For example what is soccer will tell you that " +
        "soccer is a sport played with a ball and what is \"computer "     +
        "science\" will explain to you how the \"What is?\" has "          +
        "been made."
    )

def OnBlipSubmitted(properties, context):
    """
    Invoked when a new blip is created. Here we  look for the
    'what is' string.
    """
    blip = context.GetBlipById(properties['blipId'])
    terms = extract_whatis_term(blip.GetDocument().GetText())

    # if there's no "what is" statement, don't do anything
    if not terms:
        return

    for term in terms:
        doc = blip.CreateChild().GetDocument()

        try:
            resolved = False
            for count, definition in enumerate(get_definitions(term)):
                resolved = True
                doc.AppendText(definition + "\n")
                if count > 5: break

            if not resolved:
                doc.SetText("No definition found for %s" % term)
        except urlfetch.DownloadError:
            doc.SetText(
                    "Sorry, it's not my fault but I can't access the " +
                    "dictionary service right now!")

def main():
    r = robot.Robot(ROBOT_NAME,
            image_url='http://wave-whatis.appspot.com/media/icon.png',
            version='1',
            profile_url='http://wave-whatis.appspot.com')
    r.RegisterHandler(events.WAVELET_SELF_ADDED, OnRobotAdded)
    r.RegisterHandler(events.BLIP_SUBMITTED, OnBlipSubmitted)
    r.Run()
    return True

if __name__ == '__main__':
    sys.exit(not main())

